﻿using System;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows.Storage;
using Windows.Storage.Streams;

namespace AllanSpartacus.Core.Caching
{
    public class Cache<T>
    {
        private const string FILE_EXTENSION = ".xml";
        private StorageFolder localFolder;
        private XmlSerializer serializer;
        private StorageType storageType;
        private ApplicationData appData = ApplicationData.Current;
        
        public Cache(StorageType StorageType)
        {
            serializer = new XmlSerializer(typeof(T));
            storageType = StorageType;
        }

        
        public StorageFolder LocalFolder
        {
            get { return localFolder; }
            set { localFolder = value; }
        }

        /// <summary>
        /// Saves a string to a file.
        /// </summary>
        /// <param name="content">The string content.</param>
        /// <param name="fileName">The file name.</param>
        public async void SaveJsonAsync<U>(U content, string fileName) where U: T
        {
            fileName = String.Format("{0}{1}", fileName, FILE_EXTENSION);
            localFolder = SetStorageFolder(storageType);
            StorageFile file = await localFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
            if (file != null)
            {
                if (!string.IsNullOrEmpty(content as string))
                {
                    await FileIO.WriteTextAsync(file, content as string);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public async Task<string> FindJsonAsync(string fileName)
        {
            fileName = String.Format("{0}{1}", fileName, FILE_EXTENSION);
            localFolder = SetStorageFolder(storageType);
            try
            {
                StorageFile file = await localFolder.GetFileAsync(fileName);
                string fileContent = string.Empty;
                if (file != null)
                {
                    fileContent = await FileIO.ReadTextAsync(file);
                }
                return fileContent;
            }
            catch
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Saves object to file.
        /// </summary>
        /// <param name="objectToSave">The object to save.</param>
        /// <param name="fileName">The file name.</param>
        public async void SaveAsync(T objectToSave, string fileName)
        {
            fileName = String.Format("{0}{1}", fileName, FILE_EXTENSION);
            try
            {
                if (objectToSave != null)
                {
                    StorageFolder folder = SetStorageFolder(storageType);
                    StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
                    IRandomAccessStream streamForWrite = await file.OpenAsync(FileAccessMode.ReadWrite);
                    Stream streamToSearialize = Task.Run(() => streamForWrite.AsStreamForWrite()).Result;
                    serializer.Serialize(streamToSearialize, objectToSave);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Loads the saved object from a file.
        /// </summary>
        /// <param name="fileName">The file name.</param>
        /// <returns>The saved object.</returns>
        public async Task<T> FindAsync(string fileName)
        {
            fileName = String.Format("{0}{1}", fileName, FILE_EXTENSION);
            try
            {
                StorageFolder folder = SetStorageFolder(storageType);
                StorageFile file = await folder.GetFileAsync(fileName);
                IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read);
                Stream streamToSerialize = Task.Run(() => readStream.AsStreamForRead()).Result;
                return (T)serializer.Deserialize(streamToSerialize);
            }
            catch (FileNotFoundException)
            {
                return default(T);
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Deletes a saved object.
        /// </summary>
        /// <param name="fileName">The name of the file to which the object was saved.</param>
        public async void DeleteAsync(string fileName)
        {
            fileName = String.Format("{0}{1}", fileName, FILE_EXTENSION);
            try
            {
                StorageFolder folder = SetStorageFolder(storageType);
                var file = await FindFileAsync(fileName, folder);
                if (file != null)
                {
                    await file.DeleteAsync(StorageDeleteOption.PermanentDelete);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Gets the file from a Folder.
        /// </summary>
        /// <param name="fileName">The file name.</param>
        /// <param name="folder">The folder to which the file was saved.</param>
        /// <returns>The StorageFile object.</returns>
        private async Task<StorageFile> FindFileAsync(string fileName, StorageFolder folder)
        {
            try
            {
                return await folder.GetFileAsync(fileName);
            }
            catch
            {
                return null;
            }
        }

        private StorageFolder SetStorageFolder(StorageType storageType)
        {
            StorageFolder folder;
            switch (storageType)
            {
                case StorageType.Local:
                    folder = appData.LocalFolder;
                    break;
                case StorageType.Temporary:
                    folder = appData.TemporaryFolder;
                    break;
                case StorageType.Roaming:
                    folder = appData.RoamingFolder;
                    break;
                default:
                    throw new Exception(String.Format("StorageType Error: {0}", storageType));
            }
            return folder;
        }
    }
}
